package org.nimi317.web_gis.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.nimi317.web_gis.Enum.ProvinceDataEnum;
import org.nimi317.web_gis.annotation.Page;
import org.nimi317.web_gis.context.SecurityContextHolder;
import org.nimi317.web_gis.data.AreaCodeList;
import org.nimi317.web_gis.entity.*;
import org.nimi317.web_gis.form.post.ExportPost;
import org.nimi317.web_gis.form.post.SearchPost;
import org.nimi317.web_gis.form.post.StationRenderPost;
import org.nimi317.web_gis.model.AirQualityModel;
import org.nimi317.web_gis.model.AirQualityPredict;
import org.nimi317.web_gis.service.*;
import org.nimi317.web_gis.task.AirQualityRealTask;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;

/**
 * @author thunderobot
 */
@RestController
@RequestMapping("stationQuality")
@Slf4j
@SaCheckLogin
public class StationQualityController {

    private final IStationQualityService stationQualityService;

    private final IAreaQualityService areaQualityService;

    private final ProvinceService provinceService;

    private final AreaCodeList areaCodeList;

    private final IStationService stationService;

    private final AirQualityRealTask task;

    private final AirQualityPredict predict;

    private final IUpdateService updateService;

    private final AirQualityModel qualityModel;

    public StationQualityController(IStationQualityService stationQualityService, IAreaQualityService areaQualityService, ProvinceService provinceService, AreaCodeList areaCodeList, IStationService stationService, AirQualityRealTask task, AirQualityPredict predict, IUpdateService updateService, AirQualityModel qualityModel) {
        this.stationQualityService = stationQualityService;
        this.areaQualityService = areaQualityService;
        this.provinceService = provinceService;
        this.areaCodeList = areaCodeList;
        this.stationService = stationService;
        this.task = task;
        this.predict = predict;
        this.updateService = updateService;
        this.qualityModel = qualityModel;
    }

    /**
     * 如果出于更新状态则等待更新完毕
     *
     * @return 更新状态
     */
    @GetMapping("realTime/update")
    @Deprecated
    public R<?> updateStationQuality() {
        boolean update = task.getUpdate();
        // 如果未更新时锁住
        if (!update) {
            synchronized (AirQualityRealTask.class) {
                return R.ok(update);
            }
        }
        return R.ok(update);
    }

    /**
     * 获取全国城市当前空气质量数据
     */
    @GetMapping("realTime")
    @Transactional
    public R<List<Station>> getCityQualityRealTime() {
        SecurityContextHolder.setTableName(this.areaCodeList.getTableName());
        //获取城市下所有站点
        LambdaQueryWrapper<Station> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .select(Station::getStationCode, Station::getPositionName, Station::getCityCode, Station::getLongitude, Station::getLatitude)
                .eq(Station::getStatus, true);
        List<Station> list = stationService.list(wrapper);
        // 获取站点下的当前时间数据
        LambdaQueryWrapper<StationQuality> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StationQuality::getTimestamp, updateService.getLastUpdateTime());
        List<StationQuality> qualityList = stationQualityService.list(queryWrapper);
        List<Station> stations = list
                .stream()
                .peek(station -> qualityList.stream().filter(stationQuality -> stationQuality.getStationCode().equals(station.getStationCode())).findFirst().ifPresent(station::setStationQuality))
                .filter(station -> station.getStationQuality() != null)
                .toList();
        SecurityContextHolder.remove();
        return R.ok(stations);
    }

    @PostMapping("history")
    public R<?> getHistory(@RequestBody @Validated StationRenderPost post) {
        // 这是一个一个月前的的时间
        return R.ok(stationQualityService.getHistory(post));
    }

    @GetMapping("health/{id}")
    @Deprecated
    public R<?> getHealth(@PathVariable String id) {
        return R.ok(stationQualityService.getHistory(new StationRenderPost(id, ProvinceDataEnum.Month)));
    }

    @GetMapping("predict/{id}")
    public R<?> getPredict(@PathVariable String id) {
        List<StationQuality> data = stationQualityService.getRecentQualityData(id);
        boolean model = qualityModel.hasModel(id);
        if(model) {
            List<BaseQuality> qualities = qualityModel.preparedData(id, data);
            return R.ok(qualities);
        }
        List<AirQualityPredict.Data> predictData = predict.predictData(data.stream().sorted(Comparator.comparing(StationQuality::getTimestamp)).toList());
        return R.ok(predictData);
    }

    @PostMapping("search")
    @Page
    public R<?> search(@RequestBody SearchPost post) {
        IPage<?> page =
                switch (post.getRange()) {
                    case Province -> provinceService.getHistory(Integer.valueOf(post.getId()), post.getTimeRange());
                    case City -> areaQualityService.getHistory(Integer.valueOf(post.getId()), post.getTimeRange());
                    case Station ->
                            stationQualityService.getHistory(post.getId(), post.getTimeRange(), post.getCurrentPage(), post.getPageSize());
                };
        return R.ok(page);
    }

    @PostMapping("export")
    public void export(@RequestBody @Validated ExportPost post, HttpServletResponse response) throws IOException {
        List<LocalDateTime> timeRange = post.getTimeRange();
        List<?> result = switch (post.getRange()) {
            case Province -> provinceService.getHistoryList(Integer.valueOf(post.getId()), timeRange);
            case City -> areaQualityService.getHistoryList(Integer.valueOf(post.getId()), timeRange);
            case Station -> stationQualityService.getHistory(post.getId(), timeRange);
        };
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode(post.getId(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + "-" + timeRange.get(0) + "-" + timeRange.get(1) + ".xlsx");
            // 这里需要设置不关闭流
            ServletOutputStream outputStream = response.getOutputStream();
            ExcelWriterBuilder write = switch (post.getRange()) {
                case Province -> EasyExcel.write(outputStream, ProvinceQuality.class);
                case City -> EasyExcel.write(outputStream, AreaQuality.class);
                case Station -> EasyExcel.write(outputStream, StationQuality.class);
            };
            write.autoCloseStream(Boolean.FALSE)
                    .sheet("Sheet1")
                    .doWrite(result);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            R<Object> objectR = R.fail("下载文件失败");
            response.getWriter().println(JSON.toJSONString(objectR));
        }
    }

    @GetMapping
    public R<?> getStation() {
        return R.ok(stationService.list());
    }
}
